package ru.amse.tsyganov.jumleditor.view;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

import ru.amse.tsyganov.jumleditor.model.ActivityGraph;
import ru.amse.tsyganov.jumleditor.model.State;
import ru.amse.tsyganov.jumleditor.model.Transition;
import ru.amse.tsyganov.jumleditor.properties.BooleanProperty;
import ru.amse.tsyganov.jumleditor.properties.StringProperty;

public class TransitionView<T extends Transition> extends ViewElement<Transition> {
	
	private final ActivityGraphView<? extends ActivityGraph> activityGraphView;
	
	private final List<? extends ActivePoint> points;
	
	private StringProperty guardExpressionProperty;
	
	private BooleanProperty guardProperty;
	
	private Point guardExpressionExtent;
	
	public TransitionView(T model,
			ActivityGraphView<? extends ActivityGraph> activityGraphView) {
		
		super(model);
		points = createActivePoints();
		this.activityGraphView = activityGraphView;
		initProperties();
	}
	
	@Override
	public void paint(GC gc) {
		assert gc != null;
		
		gc.setForeground(Colors.getBlackColor());
		gc.setBackground(Colors.getBlackColor());
		
		TransitionDraughtsman.drawTransition(gc, this);
		
		if (getModel().isGuard()) {
			gc.setBackground(Colors.getWhiteColor());
			
			String expression = "[" + getModel().getGuardExpression() + "]";
			
			guardExpressionExtent = gc.stringExtent(expression);
			
			gc.drawString(expression, 
					getCenterPoint().x - guardExpressionExtent.x / 2, 
					getCenterPoint().y - guardExpressionExtent.y / 2);
		}
	}

	@Override
	public void accept(Visitor v) {
		v.visitTransitionView(this);
	}
	
	@Override
	public List<? extends ActivePoint> getActivePoints() {
		if (getSelectedItems().selectedTransitionsContains(this)) {
			return points;
		} else {
			return Collections.emptyList();
		}
	}
	
	@Override
	public ActivityGraphView<? extends ActivityGraph> getActivityGraphView() {
		return activityGraphView;
	}
	
	public Point getStartPoint() {
		return getSource().getConnectionPoint(getTargetRectCenter(), false);
	}
	
	public Point getEndPoint() {
		return getTarget().getConnectionPoint(getStartPoint(), true);
	}
	
	public Point getCenterPoint() {
		Point sourceCenter = getStartPoint();
		Point targetCenter = getEndPoint();
		return new Point(
				(sourceCenter.x + targetCenter.x) / 2,
				(sourceCenter.y + targetCenter.y) / 2);
	}
	
	private StateView<? extends State> getSource() {
		StateView<? extends State> source = getModelViewMap().
			getStatesMap().get(getModel().getSource().getContainer());
		return source;
	}
	
	private StateView<? extends State> getTarget() {
		StateView<? extends State> target = getModelViewMap().
			getStatesMap().get(getModel().getTarget().getContainer());
		return target;
	}
	
	/*private Point getSourceRectCenter() {
		StateView<? extends State> source = getModelViewMap().
			getStatesMap().get(getModel().getSource().getContainer());
		return source.getCenterPoint();
	}*/
	
	private Point getTargetRectCenter() {
		StateView<? extends State> target = getModelViewMap().
			getStatesMap().get(getModel().getTarget().getContainer());
		return target.getCenterPoint();
	}
	
	private List<? extends ActivePoint> createActivePoints() {
		List<ActivePoint> points = new ArrayList<ActivePoint>(2);
		TransitionActivePoint startPoint = new TransitionActivePoint(true) {
			@Override
			public int getCurrentActivePointX() {
				return getStartPoint().x;
			}
			@Override
			public int getCurrentActivePointY() {
				return getStartPoint().y;
			}
			@Override
			public Transition getModel() {
				return TransitionView.this.getModel();
			}
			@Override
			public ActivityGraphView<? extends ActivityGraph> getContainer() {
				return activityGraphView;
			}
		};
		
		TransitionActivePoint endPoint = new TransitionActivePoint(false) {
			@Override
			public int getCurrentActivePointX() {
				return getEndPoint().x;
			}
			@Override
			public int getCurrentActivePointY() {
				return getEndPoint().y;
			}
			@Override
			public Transition getModel() {
				return TransitionView.this.getModel();
			}
			@Override
			public ActivityGraphView<? extends ActivityGraph> getContainer() {
				return activityGraphView;
			}
		};
		points.add(startPoint);
		points.add(endPoint);
		return points;
	}

	@Override
	public void paintActivePoints(GC gc) {
		if (getSelectedItems().selectedTransitionsContains(this)) {
			for (ActivePoint p : getActivePoints()) {
				p.paintActivePoint(gc);
			}
		}
	}
	
	private void initProperties() {
		
		guardProperty = new BooleanProperty("Guard") {

			@Override
			public Boolean getPropertyValue() {
				return getModel().isGuard();
			}

			@Override
			public void setPropertyValue(Boolean newValue) {
				getModel().setGuard(newValue);
				activityGraphView.getModel().fireModelChanged();
			}
						
		};
		
		super.getProperties().addProperty(guardProperty);
		
		guardExpressionProperty = new StringProperty("Guard expression") {

			@Override
			public String getPropertyValue() {
				return getModel().getGuardExpression();
			}

			@Override
			public void setPropertyValue(String newValue) {
				getModel().setGuardExpression(newValue);
			}
			
			@Override
			public boolean canModify() {
				return getModel().isGuard();
			}
			
		};
		
		super.getProperties().addProperty(guardExpressionProperty);
	}
	
	@Override
	public StringProperty getMainProperty() {
		return getModel().isGuard() ? guardExpressionProperty : null;
	}

	public Point getGuardExpressionExtent() {
		return guardExpressionExtent;
	}

	public void readFromXml(Node element) {
	}

	public void writeToXml(Element element) {
	}
}
